// RFC3550 6.5 SDES: Source Description RTCP Packet

#include "rtp-internal.h"
#include "rtp-util.h"

void rtcp_sdes_unpack(struct rtp_context *ctx, const rtcp_header_t *header, const uint8_t *ptr, size_t bytes)
{
    uint32_t i;
    struct rtcp_msg_t msg;
    struct rtp_member *member;
    const unsigned char *p, *end;

    p = ptr;
    end = ptr + bytes;
    assert(header->length >= header->rc);

    for (i = 0; i < header->rc && p + 8 /*4-ssrc + 1-PT*/ <= end; i++) {
        msg.ssrc = nbo_r32(p);
        member = rtp_member_fetch(ctx, msg.ssrc);
        if (!member) {
            // continue;
        }

        p += 4;
        while (p + 2 <= end && RTCP_SDES_END != p[0] /*PT*/) {
            msg.u.sdes.pt = p[0];
            msg.u.sdes.len = p[1];
            msg.u.sdes.data = (unsigned char *)(p + 2);
            if (p + 2 + msg.u.sdes.len > end) {
                assert(0);
                return; // error
            }

            ctx->handler.on_rtcp(ctx->cbparam, &msg);

            switch (msg.u.sdes.pt) {
            case RTCP_SDES_CNAME:
            case RTCP_SDES_NAME:
            case RTCP_SDES_EMAIL:
            case RTCP_SDES_PHONE:
            case RTCP_SDES_LOC:
            case RTCP_SDES_TOOL:
            case RTCP_SDES_NOTE:
                rtp_member_setvalue(member, msg.u.sdes.pt, msg.u.sdes.data, msg.u.sdes.len);
                break;

            case RTCP_SDES_PRIVATE:
                // assert(0);
                break;

            default:
                // assert(0);
                break;
            }

            // RFC3550 6.5 SDES: Source Description RTCP Packet
            // Items are contiguous, i.e., items are not individually padded to a 32-bit boundary.
            // Text is not null terminated because some multi-octet encodings include null octets.
            p += 2 + msg.u.sdes.len;
        }

        // RFC3550 6.5 SDES: Source Description RTCP Packet
        // The list of items in each chunk must be terminated by one or more null octets,
        // the first of which is interpreted as an item type of zero to denote the end of the list.
        // No length octet follows the null item type octet,
        // but additional null octets must be included if needed to pad until the next 32-bit boundary.
        // offset sizeof(SSRC) + sizeof(chunk type) + sizeof(chunk length)
        p = (const unsigned char *)((p - (const unsigned char *)0 + 3) / 4 * 4);
    }
}

static size_t rtcp_sdes_append_item(unsigned char *ptr, size_t bytes, rtcp_sdes_item_t *sdes)
{
    assert(sdes->data);
    if (bytes >= (size_t)sdes->len + 2) {
        ptr[0] = sdes->pt;
        ptr[1] = sdes->len;
        memcpy(ptr + 2, sdes->data, sdes->len);
    }

    return sdes->len + 2;
}

int rtcp_sdes_pack(struct rtp_context *ctx, uint8_t *ptr, int bytes)
{
    int n;
    rtcp_header_t header;

    // must have CNAME
    if (!ctx->self->sdes[RTCP_SDES_CNAME].data)
        return 0;

    header.v = 2;
    header.p = 0;
    header.pt = RTCP_SDES;
    header.rc = 1; // self only
    header.length = 0;

    n = (int)rtcp_sdes_append_item(ptr + 8, bytes - 8, &ctx->self->sdes[RTCP_SDES_CNAME]);
    if (bytes < 8 + n)
        return 8 + n;

    // RFC3550 6.3.9 Allocation of Source Description Bandwidth (p29)
    // Every third interval (15 seconds), one extra item would be included in the SDES packet
    if (0 == ctx->rtcp_cycle % 3 && ctx->rtcp_cycle / 3 > 0) // skip CNAME
    {
        assert(ctx->rtcp_cycle / 3 < RTCP_SDES_PRIVATE);
        if (ctx->self->sdes[ctx->rtcp_cycle / 3 + 1].data) // skip RTCP_SDES_END
        {
            n += (int)rtcp_sdes_append_item(ptr + 8 + n, bytes - n - 8, &ctx->self->sdes[ctx->rtcp_cycle / 3 + 1]);
            if (n + 8 > bytes)
                return n + 8;
        }
    }

    ctx->rtcp_cycle = (ctx->rtcp_cycle + 1) % 24; // 3 * SDES item number

    header.length = (uint16_t)((n + 4 + 3) / 4); // see 6.4.1 SR: Sender Report RTCP Packet
    nbo_write_rtcp_header(ptr, &header);
    nbo_w32(ptr + 4, ctx->self->ssrc);

    return (header.length + 1) * 4;
}
